/*  This file is a part of JJSched source code
    JJSched - Java Jabber Scheduler - a simple Jabber scheduling application
    Copyright (C) 2008 Cestmir "CZestmyr" Houska

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/> */

package czestmyr.jjsched;

import java.text.*;
import java.io.*;

/**
 * Matches a string to an other string and returnes the number that belongs to the matched string.
 *
 * This allows for using strings in switch statements without having to iterate over the string many times.
 * Works only for english alphabet strings. Also, all strings are converted to lowercase before matching.
 */
public class StringMatcher<V> {
	StringMatcher() {
		root = new TrieNode();
	}

	/**
     * Adds a string with its ID number to the list of matches
     *
     * @param 	value 		ID of the match (should be positive because non-positive IDs are reserved
	 * 						for indication of not being able to find a match)
	 * @param	string		The actual match. I.e. while matching later, the string should equal to
	 *						this to be matched.
     */
	public void addMatch(V value, String string) {
		String lcString = string.toLowerCase();

		StringCharacterIterator it = new StringCharacterIterator(lcString);
		TrieNode node = root;

		//Traverse the trie and create new nodes as necessary
		for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
			node = node.createSibling(c);
			if (node == null) return; //createSibling could fail in case of incorrect input
		}

		node.data = (Object)value;
	}

	/**
	 * Returns an ID of the string
	 *
	 * @param	string		The input string that will be matched with the list of matches
	 * @return				ID of the match that was added with the {@link #addMatch(V, String) addMatch} method.
	 *						Zero if no match could be found
	 */
	@SuppressWarnings(value={"unchecked"})
	public V match(String string) {
		String lcString = string.toLowerCase();

		StringCharacterIterator it = new StringCharacterIterator(lcString);
		TrieNode node = root;

		//Traverse the trie without creating new nodes
		for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
			node = node.getSibling(c);
			if (node == null) return null; //Fail if ended in a dead-end
		}

		return (V)node.data;
	}

	private TrieNode root;
}

class TrieNode {
	TrieNode() {
		siblings = new TrieNode[26];
	}

	public TrieNode createSibling(char c) {
		TrieNode ret = getSibling(c);
		if (ret == null) {
			ret = new TrieNode();
			setSibling(c, ret);
		}
		return ret;
	}

	public TrieNode getSibling(char c) {
		int pos = c - 'a';
		if (pos < 0 || pos >= 26)
			return null;
		else return siblings[pos];
	}

	public void setSibling(char c, TrieNode newValue) {
		int pos = c - 'a';
		if (pos < 0 || pos >= 26)
			return;
		siblings[pos] = newValue;
	}

	public Object data;

	private TrieNode siblings[];
}

